package cn.com.zero.file.util;

import cn.com.zero.file.service.ZeroMultipartFile;
import cn.com.zero.file.vo.FileInfo;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.validation.constraints.NotEmpty;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.UUID;

/**
 * @author liuxh
 * @version 1.0.0
 * @Description 文件工具类
 * @createTime 2022/9/13
 */
@Component
public class FileUtil {


    /**
     * 允许上传的文件扩展名
     */
    @NotEmpty
    @Value("${zero.file.allowed-extension}")
    private String[] allowedExtension;

    /**
     * 构建文件基本信息
     *
     * @param file         文件
     * @param relativePath 相对路径
     * @return 文件信息
     */
    public static FileInfo getFileInfo(MultipartFile file, String relativePath) {
        FileInfo fileInfo = new FileInfo();
        fileInfo.setOriginalName(file.getOriginalFilename());
        fileInfo.setContentType(file.getContentType());
        String extension = getExtension(file);
        fileInfo.setExtension(extension);
        fileInfo.setIsImg(fileInfo.getContentType().startsWith("image/"));
        fileInfo.setSize(file.getSize());
        String relativeFilePath = getRelativeFilePath(extension, relativePath);
        fileInfo.setRelativeFilePath(relativeFilePath);
        return fileInfo;
    }

    /**
     * 获取文件名的后缀
     *
     * @param file 文件
     * @return 后缀名
     */
    public static String getExtension(MultipartFile file) {
        String extension = FilenameUtils.getExtension(file.getOriginalFilename());
        if (!StringUtils.hasLength(extension)) {
            extension = file.getContentType();
        }
        return extension;
    }

    /**
     * 获取文件名的后缀
     *
     * @param relativeFilePath 文件相对路径 例： test/pic/dhowyshd.jpg
     * @return 后缀名  例：jpg
     */
    public static String getExtension(String relativeFilePath) {
        if (StringUtils.hasLength(relativeFilePath)) {
            int index = relativeFilePath.indexOf(".");
            return relativeFilePath.substring(++index);
        }
        return null;
    }


    /**
     * 构建相对文件路径
     *
     * @param file         文件
     * @param relativePath 相对路径
     * @return 相对路径
     */
    public static String getRelativeFilePath(MultipartFile file, String relativePath) {
        String extension = getExtension(file);
        return getRelativeFilePath(extension, relativePath);
    }

    /**
     * 构建相对文件路径
     *
     * @param extension    文件类型
     * @param relativePath 相对路径  例：test/pic/ 注意：传路径的时候分隔符只能用 "/"，不能用"\\\\"或File.separator，否者跨平台调用minio的时候有问题，分隔符可能会被解析成%5c
     * @return 相对路径
     */
    public static String getRelativeFilePath(String extension, String relativePath) {
        StringBuilder stringBuilder = new StringBuilder();
        // 随机文件名
        String fileName = randomFileName(extension);
        if (!StringUtils.hasLength(relativePath)) {
            stringBuilder.append(fileName);
        } else {
            stringBuilder.append(relativePath).append(fileName);
        }
        return stringBuilder.toString();
    }

    /**
     * 随机生成文件名
     *
     * @param extension 文件类型
     * @return 相对路径
     */
    public static String randomFileName(String extension) {
        String str = UUID.randomUUID().toString();
        str = str.replaceAll("-", "");
        return str + "." + extension;
    }


    /**
     * 压缩图片 如果宽、高只传一个值时，另一个按等比压缩；如果两个都有传值，则按要求压缩；
     *
     * @param multipartFile 图片流
     * @param width         指定压缩后图片的宽
     * @param height        指定压缩后图片的高
     * @return 压缩后的图片流
     */
    public static MultipartFile compressImg(MultipartFile multipartFile, Integer width, Integer height) {
        // 压缩图片
        BufferedImage newImg = null;
        String extension = getExtension(multipartFile);
        String originalFilename = multipartFile.getOriginalFilename();
        try {
            // 创建file临时文件
            File file = File.createTempFile(UUID.randomUUID().toString(), null);
            FileUtils.copyInputStreamToFile(multipartFile.getInputStream(), file);
            Image imageSrc = Toolkit.getDefaultToolkit().getImage(file.getPath());
            BufferedImage srcImage = toBufferedImage(imageSrc);

            // 如果上传的宽、高正好等于原图，则直接上传原图
            if (width == null && height == srcImage.getHeight()) {
                return multipartFile;
            }
            if (height == null && width == srcImage.getWidth()) {
                return multipartFile;
            }
            if (height != null && width != null &&
                    height == srcImage.getHeight() && width == srcImage.getWidth()) {
                return multipartFile;
            }

            // 新图片的宽度和高度计算
            Integer newWidth = width;
            Integer newHeight = height;
            if (newHeight == null) {
                newHeight = (int) ((double) srcImage.getHeight() * (double) width / (double) srcImage.getWidth());
            } else if (newWidth == null) {
                newWidth = (int) ((double) srcImage.getWidth() * (double) height / (double) srcImage.getHeight());
            }

            // 判断输入图片的类型
            if (srcImage.getType() != BufferedImage.TYPE_BYTE_INDEXED) {
                newImg = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
            } else {
                newImg = srcImage;
            }
            // 根据图片尺寸压缩比得到新图的尺寸
            newImg.getGraphics().drawImage(srcImage.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH), 0, 0, null);
            newImg.getGraphics().dispose();
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            ImageIO.write(newImg, extension, os);
            // 压缩后文件
            return new ZeroMultipartFile(multipartFile.getName(), originalFilename, multipartFile.getContentType(), os.toByteArray());
        } catch (Exception e) {
            throw new RuntimeException("压缩处理上传图片" + originalFilename + "错误", e);
        } finally {
            if (newImg != null) {
                try {
                    newImg.flush();
                } catch (Exception ignored) {
                }
            }
        }
    }

    /**
     * <b>方法说明： 处理图片变红问题
     *
     * @param image 指定图片
     * @return BufferedImage
     */
    public static BufferedImage toBufferedImage(Image image) {
        if (image instanceof BufferedImage) {
            return (BufferedImage) image;
        }
        // This code ensures that all the pixels in the image are loaded
        image = new ImageIcon(image).getImage();
        BufferedImage bimage = null;
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        try {
            int transparency = Transparency.OPAQUE;
            GraphicsDevice gs = ge.getDefaultScreenDevice();
            GraphicsConfiguration gc = gs.getDefaultConfiguration();
            bimage = gc.createCompatibleImage(image.getWidth(null), image.getHeight(null), transparency);
        } catch (HeadlessException e) {
            // The system does not have a screen
        }
        if (bimage == null) {
            // Create a buffered image using the default color model
            int type = BufferedImage.TYPE_INT_RGB;
            bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
        }
        // Copy image to buffered image
        Graphics g = bimage.createGraphics();
        // Paint the image onto the buffered image
        g.drawImage(image, 0, 0, null);
        g.dispose();
        return bimage;
    }


    /**
     * 是否允许的文件类型
     *
     * @param extension        文件类型
     * @param allowedExtension 允许的文件类型集合
     * @return true：是 false：否
     */
    public static boolean isAllowedExtension(String extension, String[] allowedExtension) {
        for (String str : allowedExtension) {
            if (str.equalsIgnoreCase(extension)) {
                return true;
            }
        }
        return false;
    }
}
